package com.mp3.player.views
{
	import com.mp3.player.events.TrackEvent;
	import com.mp3.player.managers.TrackManager;
	import com.mp3.player.skins.defaultSkin.DefaultPlayerSkin;
	import com.mp3.player.vos.Track;
	import com.mp3.player.vos.TrackList;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ArrayList;
	import mx.events.FlexEvent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	
	import spark.components.Button;
	import spark.components.ToggleButton;
	import spark.components.mediaClasses.MuteButton;
	import spark.components.mediaClasses.ScrubBar;
	import spark.components.mediaClasses.VolumeBar;
	import spark.components.supportClasses.SkinnableComponent;
	import spark.components.supportClasses.SliderBase;
	import spark.components.supportClasses.TextBase;
	
	
	/**
	 *  Dispatched when the user presses the add button control on playlist.
	 *
	 *  @eventType com.mp3.player.events.TrackListEvent.ADD
	 */
	[Event(name="add", type="com.mp3.player.events.TrackListEvent")]
	
	/**
	 *  Dispatched when the user presses the save button control on playlist.
	 *
	 *  @eventType com.mp3.player.events.TrackListEvent.SAVE
	 */
	[Event(name="save", type="com.mp3.player.events.TrackListEvent")]
	
	/**
	 * Player that can play mp3 files. 
	 */	
	public class Player extends SkinnableComponent
	{
		/**
		 * @private
		 * This static function needs to set default styles to the component 
		 * without creating separate css file.
		 */
		private static function initializeStyles():void
		{
			var styles:CSSStyleDeclaration = StyleManager.getStyleManager(null).getStyleDeclaration("com.mp3.player.views.Player");
			if(!styles)
				styles = new CSSStyleDeclaration();
			
			styles.defaultFactory = function():void
			{	
				// default styles for the component
				this.skinClass = DefaultPlayerSkin;
			}
			StyleManager.getStyleManager(null).setStyleDeclaration("com.mp3.player.views.Player", styles, false);
		}
		initializeStyles();
		
		/**
		 * Constructor. 
		 */		
		public function Player()
		{
			super();
			
			trackManager.addEventListener(TrackEvent.FINISH, trackFinishedHandler);
			trackManager.addEventListener(TrackEvent.INFO_CHANGED, onInfoChanged);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Skin parts
		//
		//--------------------------------------------------------------------------		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the play/pause button. 
		 */
		public var playPauseButton:ToggleButton;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the play button. 
		 */
		public var playButton:Button;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the pause button. 
		 */
		public var pauseButton:Button;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the stop button. 
		 */
		public var stopButton:Button;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the next button. 
		 */
		public var nextButton:Button;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the prev button. 
		 */
		public var prevButton:Button;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the playlist. 
		 */
		public var playlist:Playlist;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the track info. 
		 */
		public var trackInfo: TrackInfo;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the advertising panel. 
		 */
		public var advertPanel: AdvertPanel;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the volume slider. 
		 */
		public var volumeSlider:SliderBase;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the mute button. 
		 */
		public var muteButton:MuteButton;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the scrubBar. 
		 */
		public var scrubBar:ScrubBar;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the shuffle button. 
		 */
		public var shuffleButton:ToggleButton;
		
		[SkinPart(required="false")]
		/**
		 *  A skin part that defines the repeat button. 
		 */
		public var repeatButton:ToggleButton;
		
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 * Track manager. 
		 */		
		private var trackManager:TrackManager = new TrackManager();
		
		/**
		 * Storage for trackList.
		 */		
		private var _trackList:TrackList = new TrackList("default");

		[Bindable("trackListChanged")]
		/**
		 * @private
		 * Track list. 
		 */
		public function get trackList():TrackList
		{
			return _trackList;
		}
		
		[Bindable]
		/**
		 * Total bytes in track. 
		 */		
		public var bytesTotal:Number = 0;
		
		[Bindable]
		/**
		 * Loaded bytes. 
		 */	
		public var bytesLoaded:Number = 0;
		
		[Bindable]
		/**
		 * Duration of track (miliseconds). 
		 */		
		public var trackLength:Number = 0;
		
		[Bindable]
		/**
		 * Current playing position (miliseconds). 
		 */		
		public var currentPosition:Number = 0;
		
		/**
		 * Current played track. 
		 */		
		private var _currentTrack: Track;
		private var _currentTrackChanged: Boolean = false;
		
		[Bindable]
		/**
		 * Current  Track
		 */		
		public function get currentTrack(): Track
		{
			return _currentTrack;
		}
		public function set currentTrack(value: Track): void
		{
			if (_currentTrack != null)
			{
				_currentTrack.selected = false;
			}
			_currentTrack = value;
			
			if (_currentTrack != null)
			{
				_currentTrack.selected = true;
			}
			_currentTrackChanged = true;
			invalidateProperties();
		}
		
		/**
		 * Source to TLF xml file to show in advertasing panel
		 */
		private var _advertSource: String;
		
		public function get advertSource(): String
		{
			return _advertSource;
		}
		
		public function set advertSource(value: String): void
		{
			_advertSource = value;
			if (advertPanel)
			{
				advertPanel.source = _advertSource;
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Add track to playlist. 
		 * @param track Track
		 */		
		public function addTrack(track:Track):void
		{
			trackList.addTrack(track);
		}
		
		/**
		 * Add tracks to playlist. 
		 * @param list TrackList
		 */		
		public function addTrackList(list: TrackList):void
		{
			trackList.addTracks(list.tracks);
		}
		
		/**
		 * Set new track list.  
		 * @param list TrackList
		 */		
		public function newTrackList(list: TrackList):void
		{
			_trackList = list;
			
			if(playlist)
				playlist.trackList = trackList;
			
			if(shuffleButton)
				shuffleButton.selected = _trackList.shuffle;
			
			if(repeatButton)
				repeatButton.selected = _trackList.repeat;
			
			dispatchEvent(new Event("trackListChanged"));
		}
		
		/**
		 * Get current track list. 
		 * @return TrackList
		 */		
		public function getTrackList():TrackList
		{
			return trackList;
		}
		
		/**
		 * Play track. 
		 * 
		 * @param track Track. If null then resume plying current track.
		 */		
		public function play(track:Track=null):void
		{
			if(track)
			{
				trackList.selectTrack(track);
				loadTrack(track);
			}
			
			trackManager.play();
			invalidateProperties();
			if(!hasEventListener(Event.ENTER_FRAME))
				addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * Pause track. 
		 */		
		public function pause():void
		{
			trackManager.pause();
			invalidateProperties();
		}
		
		/**
		 * Stop track. 
		 */		
		public function stop():void
		{
			pause();
			
			trackManager.stop();
			trackList.selectTrack(null);
			currentTrack = null;
			
			updateTrackProperties();
		}
		
		/**
		 * play next track. 
		 */		
		public function next():void
		{
			pause();
			trackList.next();
			loadTrack(trackList.selectedTrack);
			play();
		}
		
		/**
		 * Play previous track. 
		 */		
		public function prev():void
		{
			pause();
			trackList.prev();
			loadTrack(trackList.selectedTrack);
			play();
		}
		
		/**
		 * @private
		 * 
		 * Load track and set it as current.
		 * 
		 * @param track Track
		 */		
		private function loadTrack(track:Track):void
		{
			currentTrack = track;
			trackManager.loadSound(track);
		}
		
		/**
		 * @private
		 * 
		 * Update track properties.
		 * 
		 * @param track Track
		 */		
		private function updateTrackProperties():void
		{
			bytesTotal = trackManager.bytesTotal;
			bytesLoaded = trackManager.bytesLoaded;
			trackLength = trackManager.trackLength;
			currentPosition = trackManager.currentPosition;
			
			if(scrubBar && !scrubBarIsInUse)
			{
				scrubBar.maximum = trackLength/1000;
				scrubBar.value = currentPosition/1000
				scrubBar.loadedRangeEnd = bytesTotal > 0 ? (bytesLoaded/bytesTotal)*scrubBar.maximum : 0;
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Lyfe Cycle
		//
		//--------------------------------------------------------------------------
		
		/**
		 * @inheritDoc 
		 */		
		override protected function partAdded(partName:String, instance:Object):void
		{
			super.partAdded(partName, instance);
			
			switch(instance)
			{
				case playPauseButton:
					playPauseButton.addEventListener(MouseEvent.CLICK, playPauseClickHandler);
					break;
				
				case playButton:
					playButton.addEventListener(MouseEvent.CLICK, playClickHandler);
					break;
				
				case pauseButton:
					pauseButton.addEventListener(MouseEvent.CLICK, pauseClickHandler);
					break;
				
				case stopButton:
					stopButton.addEventListener(MouseEvent.CLICK, stopClickHandler);
					break;
				
				case volumeSlider:
					volumeSlider.addEventListener(Event.CHANGE, volumeChangeHandler);
					break;
				
				case muteButton:
					muteButton.addEventListener(FlexEvent.MUTED_CHANGE, volumeMutedChangeHandler);
					break;
				
				case scrubBar:
					scrubBar.enabled = !(currentTrack == null);
					scrubBar.addEventListener(FlexEvent.CHANGE_START, scrubBarChangeStartHandler);
					scrubBar.addEventListener(FlexEvent.CHANGE_END, scrubBarChangeHandler);
					break;
				
				case playlist:
					playlist.trackList = trackList;
					playlist.addEventListener(TrackEvent.PLAY, onTrackPlay);
					break;
				
				case nextButton:
					nextButton.addEventListener(MouseEvent.CLICK, nextButtonClickHandler);
					break;
				
				case prevButton:
					prevButton.addEventListener(MouseEvent.CLICK, prevButtonClickHandler);
					break;
				
				case shuffleButton:
					shuffleButton.addEventListener(MouseEvent.CLICK, shuffleClickHandler);
					if(_trackList)
						shuffleButton.selected = _trackList.shuffle;
					break;
				
				case repeatButton:
					repeatButton.addEventListener(MouseEvent.CLICK, repeatClickHandler);
					if(_trackList)
						repeatButton.selected = _trackList.repeat;
					break;
				
				case trackInfo:
					trackInfo.track = currentTrack;
					break;
				
				case advertPanel:
					advertPanel.source = advertSource;
					break;
			}
		}
		
		/**
		 * @inheritDoc 
		 */	
		override protected function partRemoved(partName:String, instance:Object):void
		{
			super.partRemoved(partName, instance);
			
			switch(instance)
			{
				case playPauseButton:
					playPauseButton.removeEventListener(MouseEvent.CLICK, playPauseClickHandler);
					invalidateProperties();
					break;
				
				case playButton:
					playButton.removeEventListener(MouseEvent.CLICK, playClickHandler);
					break;
				
				case pauseButton:
					pauseButton.removeEventListener(MouseEvent.CLICK, pauseClickHandler);
					break;
				
				case stopButton:
					stopButton.removeEventListener(MouseEvent.CLICK, stopClickHandler);
					break;
				
				case volumeSlider:
					volumeSlider.removeEventListener(Event.CHANGE, volumeChangeHandler);
					break;
				
				case muteButton:
					muteButton.removeEventListener(FlexEvent.MUTED_CHANGE, volumeMutedChangeHandler);
					break;
				
				case scrubBar:
					scrubBar.removeEventListener(Event.CHANGE, scrubBarChangeHandler);
					break;
				
				case nextButton:
					nextButton.removeEventListener(MouseEvent.CLICK, nextButtonClickHandler);
					break;
				
				case prevButton:
					prevButton.removeEventListener(MouseEvent.CLICK, prevButtonClickHandler);
					break;
				
				case shuffleButton:
					shuffleButton.removeEventListener(MouseEvent.CLICK, shuffleClickHandler);
					break;
				
				case repeatButton:
					repeatButton.removeEventListener(MouseEvent.CLICK, repeatClickHandler);
					break;
			}
		}
		
		/**
		 * @inheritDoc 
		 */	
		override protected function commitProperties():void
		{
			super.commitProperties();
			
			if(playPauseButton)
				playPauseButton.selected = trackManager.isPlaying;
			
			if(volumeSlider && !muted)
				volumeSlider.value = trackManager.volume;
			
			if (_currentTrackChanged)
			{
				if (playlist != null)
				{
					playlist.currentTrack = currentTrack;
				}
				
				scrubBar.enabled = !(currentTrack == null);
				
				_currentTrackChanged = false;
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		/**
		 * @private 
		 * 
		 * Play/Pause button click handler.
		 *  
		 * @param event MouseEvent
		 */		
		private function playPauseClickHandler(event:MouseEvent):void
		{
			if((event.target as ToggleButton).selected)
				play();
			else
				pause();
		}
		
		/**
		 * @private 
		 * 
		 * Play button click handler.
		 *  
		 * @param event MouseEvent
		 */		
		private function playClickHandler(event:MouseEvent):void
		{
			if(trackManager.isPlaying)
				play(currentTrack);
			else
				play();
		}
		
		/**
		 * @private 
		 * 
		 * Pause button click handler.
		 *  
		 * @param event MouseEvent
		 */		
		private function pauseClickHandler(event:MouseEvent):void
		{
			pause();
		}
		
		/**
		 * @private 
		 * 
		 * Stop button click handler.
		 *  
		 * @param event MouseEvent
		 */		
		private function stopClickHandler(event:MouseEvent):void
		{
			stop();
		}
		
		/**
		 * @private
		 * 
		 * Flag that indicates whether scrubBar is used (dragged by user). 
		 */		
		private var scrubBarIsInUse:Boolean = false;
		
		/**
		 * @private
		 * 
		 * Enter frame handler
		 *  
		 * @param event Event
		 * 
		 */		
		private function enterFrameHandler(event:Event):void
		{
			updateTrackProperties();
		}
		
		/**
		 * @private
		 * 
		 * Flag that indicates whether volume is mutted. 
		 */		
		private var muted:Boolean = false;
		
		/**
		 * @private
		 * 
		 * Volume change handler.
		 * 
		 * @param event Event
		 */		
		private function volumeChangeHandler(event:Event):void
		{
			if(!muted)
			{
				trackManager.volume = volumeSlider.value;
				
				if(muteButton)
					muteButton.volume = volumeSlider.value;
			}
		}
		
		/**
		 * @private
		 * 
		 * Volume muted handler.
		 * 
		 * @param event FlexEvent
		 */		
		private function volumeMutedChangeHandler(event:FlexEvent):void
		{
			muted = event.target.muted;
			trackManager.volume = muted ? 0 : volumeSlider.value;
		}
		
		/**
		 * @private
		 * 
		 * Scrub bar change handler.
		 *  
		 * @param event Event
		 */		
		private function scrubBarChangeHandler(event:Event):void
		{
			trackManager.play(scrubBar.value * 1000);
			scrubBarIsInUse = false;
		}
		
		/**
		 * @private
		 * 
		 * Scrub bar start changing handler.
		 *  
		 * @param event Event
		 */		
		private function scrubBarChangeStartHandler(event:Event):void
		{
			scrubBarIsInUse = true;
		}
		
		/**
		 * @private
		 * 
		 * Next button click handler
		 *  
		 * @param event MouseEvent
		 */		
		private function nextButtonClickHandler(event:MouseEvent):void
		{
			next();
		}
		
		/**
		 * @private
		 * 
		 * Previous button click handler
		 *  
		 * @param event MouseEvent
		 */	
		private function prevButtonClickHandler(event:MouseEvent):void
		{
			prev();
		}
		
		/**
		 * @private
		 * 
		 * Shuffle button click handler
		 *  
		 * @param event MouseEvent
		 */	
		private function shuffleClickHandler(event:MouseEvent):void
		{
			trackList.shuffle = event.target.selected;
		}
		
		/**
		 * @private
		 * 
		 * Repeat button click handler
		 *  
		 * @param event MouseEvent
		 */	
		private function repeatClickHandler(event:MouseEvent):void
		{
			trackList.repeat = event.target.selected;
		}
		
		/**
		 * @private
		 * 
		 * Playing track finish handler.
		 * 
		 * @param event TrackEvent
		 */		
		private function trackFinishedHandler(event:TrackEvent):void
		{
			next();
		}
		
		/**
		 * @private
		 * 
		 * Play track handler(double click on play list)
		 * @param event
		 * 
		 */		
		private function onTrackPlay(event: TrackEvent): void
		{
			play(event.track);
		}
		
		/**
		 * Track Info change handler 
		 * @param event
		 * 
		 */		
		private function onInfoChanged(event: TrackEvent): void
		{
			if (trackInfo)
			{
				trackInfo.track = (event.track);
			}
		}	
	}
}